home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2005 October / PCWOCT05.iso / Software / FromTheMag / XAMPP 1.4.14 / xampp-win32-1.4.14-installer.exe / xampp / php / pear / File / Passwd / Unix.php < prev   
PHP Script  |  2004-10-01  |  19KB  |  650 lines

  1. <?php
  2. // +----------------------------------------------------------------------+
  3. // | PEAR :: File :: Passwd :: Unix                                       |
  4. // +----------------------------------------------------------------------+
  5. // | This source file is subject to version 3.0 of the PHP license,       |
  6. // | that is available at http://www.php.net/license/3_0.txt              |
  7. // | If you did not receive a copy of the PHP license and are unable      |
  8. // | to obtain it through the world-wide-web, please send a note to       |
  9. // | license@php.net so we can mail you a copy immediately.               |
  10. // +----------------------------------------------------------------------+
  11. // | Copyright (c) 2003-2004 Michael Wallner <mike@iworks.at>             |
  12. // +----------------------------------------------------------------------+
  13. //
  14. // $Id: Unix.php,v 1.15 2004/06/07 19:19:47 mike Exp $
  15.  
  16. /**
  17. * Manipulate standard Unix passwd files.
  18. * @author   Michael Wallner <mike@php.net>
  19. * @package  File_Passwd
  20. */
  21.  
  22. /**
  23. * Requires File::Passwd::Common
  24. */
  25. require_once 'File/Passwd/Common.php';
  26.  
  27. /**
  28. * Manipulate standard Unix passwd files.
  29. * <kbd><u>Usage Example:</u></kbd>
  30. * <code>
  31. *   $passwd = &File_Passwd::factory('Unix');
  32. *   $passwd->setFile('/my/passwd/file');
  33. *   $passwd->load();
  34. *   $passwd->addUser('mike', 'secret');
  35. *   $passwd->save();
  36. * </code>
  37. * <kbd><u>Output of listUser()</u></kbd>
  38. * # using the 'name map':
  39. * <pre>
  40. *      array
  41. *       + user  => array
  42. *                   + pass  => crypted_passwd or 'x' if shadowed
  43. *                   + uid   => user id
  44. *                   + gid   => group id
  45. *                   + gecos => comments
  46. *                   + home  => home directory
  47. *                   + shell => standard shell
  48. * </pre>
  49. * # without 'name map':
  50. * <pre>
  51. *      array
  52. *       + user  => array
  53. *                   + 0  => crypted_passwd
  54. *                   + 1  => ...
  55. *                   + 2  => ...
  56. * </pre>
  57. * @author   Michael Wallner <mike@php.net>
  58. * @package  File_Passwd
  59. * @version  $Revision: 1.15 $
  60. * @access   public
  61. */
  62. class File_Passwd_Unix extends File_Passwd_Common
  63. {
  64.     /**
  65.     * A 'name map' wich refer to the extra properties
  66.     *
  67.     * @var array
  68.     * @access private
  69.     */
  70.     var $_map = array('uid', 'gid', 'gecos', 'home', 'shell');
  71.     
  72.     /**
  73.     * Whether to use the 'name map' or not
  74.     *
  75.     * @var boolean
  76.     * @access private
  77.     */
  78.     var $_usemap = true;
  79.     
  80.     /**
  81.     * Whether the passwords of this passwd file are shadowed in another file
  82.     *
  83.     * @var boolean
  84.     * @access private
  85.     */
  86.     var $_shadowed = false;
  87.     
  88.     /**
  89.     * Encryption mode, either md5 or des
  90.     *
  91.     * @var string
  92.     * @access private
  93.     */
  94.     var $_mode = 'des';
  95.     
  96.     /**
  97.     * Supported encryption modes
  98.     * 
  99.     * @var array
  100.     * @access private
  101.     */
  102.     var $_modes = array('md5' => 'md5', 'des' => 'des');
  103.     
  104.     /**
  105.     * Constructor
  106.     *
  107.     * @access public
  108.     * @param  string    $file   path to passwd file
  109.     */
  110.     function File_Passwd_Unix($file = 'passwd')
  111.     {
  112.         parent::__construct($file);
  113.     }
  114.     
  115.     /**
  116.     * Fast authentication of a certain user
  117.     * 
  118.     * Returns a PEAR_Error if:
  119.     *   o file doesn't exist
  120.     *   o file couldn't be opened in read mode
  121.     *   o file couldn't be locked exclusively
  122.     *   o file couldn't be unlocked (only if auth fails)
  123.     *   o file couldn't be closed (only if auth fails)
  124.     *   o invalid encryption mode <var>$mode</var> was provided
  125.     *
  126.     * @static   call this method statically for a reasonable fast authentication
  127.     * @access   public
  128.     * @return   mixed   true if authenticated, false if not or PEAR_Error
  129.     * @param    string  $file   path to passwd file
  130.     * @param    string  $user   user to authenticate
  131.     * @param    string  $pass   plaintext password
  132.     * @param    string  $mode   encryption mode to use (des or md5)
  133.     */
  134.     function staticAuth($file, $user, $pass, $mode)
  135.     {
  136.         $line = File_Passwd_Common::_auth($file, $user);
  137.         if (!$line || PEAR::isError($line)) {
  138.             return $line;
  139.         }
  140.         list(,$real)= explode(':', $line);
  141.         $crypted    = File_Passwd_Unix::_genPass($pass, $real, $mode);
  142.         if (PEAR::isError($crypted)) {
  143.             return $crypted;
  144.         }
  145.         return ($crypted === $real);
  146.     }
  147.     
  148.     /**
  149.     * Apply changes an rewrite passwd file
  150.     *
  151.     * Returns a PEAR_Error if:
  152.     *   o directory in which the file should reside couldn't be created
  153.     *   o file couldn't be opened in write mode
  154.     *   o file couldn't be locked exclusively
  155.     *   o file couldn't be unlocked
  156.     *   o file couldn't be closed
  157.     * 
  158.     * @throws PEAR_Error
  159.     * @access public
  160.     * @return mixed true on success or PEAR_Error
  161.     */
  162.     function save()
  163.     {
  164.         $content = '';
  165.         foreach ($this->_users as $user => $array){
  166.             $pass   = array_shift($array);
  167.             $extra  = implode(':', $array);
  168.             $content .= $user . ':' . $pass;
  169.             if (!empty($extra)) {
  170.                 $content .= ':' . $extra;
  171.             }
  172.             $content .= "\n";
  173.         }
  174.         return $this->_save($content);
  175.     }
  176.     
  177.     /**
  178.     * Parse the Unix password file
  179.     *
  180.     * Returns a PEAR_Error if passwd file has invalid format.
  181.     * 
  182.     * @throws PEAR_Error
  183.     * @access public
  184.     * @return mixed true on success or PEAR_Error
  185.     */
  186.     function parse()
  187.     {
  188.         $this->_users = array();
  189.         foreach ($this->_contents as $line){
  190.             $parts = explode(':', $line);
  191.             if (count($parts) < 2) {
  192.                 return PEAR::raiseError(
  193.                     FILE_PASSWD_E_INVALID_FORMAT_STR,
  194.                     FILE_PASSWD_E_INVALID_FORMAT
  195.                 );
  196.             }
  197.             $user = array_shift($parts);
  198.             $pass = array_shift($parts);
  199.             if ($pass == 'x') {
  200.                 $this->_shadowed = true;
  201.             }
  202.             $values = array();
  203.             if ($this->_usemap) {
  204.                 $values['pass'] = $pass;
  205.                 foreach ($parts as $i => $value){
  206.                     if (isset($this->_map[$i])) {
  207.                         $values[$this->_map[$i]] = $value;
  208.                     } else {
  209.                         $values[$i+1] = $value;
  210.                     }
  211.                 }
  212.             } else {
  213.                 $values = array_merge(array($pass), $parts);
  214.             }
  215.             $this->_users[$user] = $values;
  216.             
  217.         }
  218.         $this->_contents = array();
  219.         return true;
  220.     }
  221.     
  222.     /**
  223.     * Set the encryption mode
  224.     * 
  225.     * Supported encryption modes are des and md5.
  226.     * 
  227.     * Returns a PEAR_Error if supplied encryption mode is not supported.
  228.     *
  229.     * @see      setMode()
  230.     * @see      listModes()
  231.     * 
  232.     * @throws   PEAR_Error
  233.     * @access   public
  234.     * @return   mixed   true on succes or PEAR_Error
  235.     * @param    string  $mode   encryption mode to use; either md5 or des
  236.     */
  237.     function setMode($mode)
  238.     {
  239.         $mode = strToLower($mode);
  240.         if (!isset($this->_modes[$mode])) {
  241.             return PEAR::raiseError(
  242.                 sprintf(FILE_PASSWD_E_INVALID_ENC_MODE_STR, $mode),
  243.                 FILE_PASSWD_E_INVALID_ENC_MODE
  244.             );
  245.         }
  246.         $this->_mode = $mode;
  247.         return true;
  248.     }
  249.     
  250.     /** 
  251.     * Get supported encryption modes
  252.     *
  253.     * <pre>
  254.     *   array
  255.     *    + md5
  256.     *    + des
  257.     * </pre>
  258.     * 
  259.     * @see      setMode()
  260.     * @see      getMode()
  261.     * 
  262.     * @access   public
  263.     * @return   array
  264.     */
  265.     function listModes()
  266.     {
  267.         return $this->_modes;
  268.     }
  269.  
  270.     /**
  271.     * Get actual encryption mode
  272.     *
  273.     * @see      listModes()
  274.     * @see      setMode()
  275.     * 
  276.     * @access   public
  277.     * @return   string
  278.     */
  279.     function getMode()
  280.     {
  281.         return $this->_mode;
  282.     }
  283.     
  284.     /**
  285.     * Whether to use the 'name map' of the extra properties or not
  286.     * 
  287.     * Default Unix passwd files look like:
  288.     * <pre>
  289.     * user:password:user_id:group_id:gecos:home_dir:shell
  290.     * </pre>
  291.     * 
  292.     * The default 'name map' for properties except user and password looks like:
  293.     *   o uid
  294.     *   o gid
  295.     *   o gecos
  296.     *   o home
  297.     *   o shell
  298.     * 
  299.     * If you want to change the naming of the standard map use 
  300.     * File_Passwd_Unix::setMap(array()).
  301.     *
  302.     * @see      setMap()
  303.     * @see      getMap()
  304.     * 
  305.     * @access   public
  306.     * @return   boolean always true if you set a value (true/false) OR
  307.     *                   the actual value if called without param
  308.     * 
  309.     * @param    boolean $bool   whether to use the 'name map' or not
  310.     */
  311.     function useMap($bool = null)
  312.     {
  313.         if (is_null($bool)) {
  314.             return $this->_usemap;
  315.         }
  316.         $this->_usemap = (bool) $bool;
  317.         return true;
  318.     }
  319.     
  320.     /**
  321.     * Set the 'name map' to use with the extra properties of the user
  322.     * 
  323.     * This map is used for naming the associative array of the extra properties.
  324.     *
  325.     * Returns a PEAR_Error if <var>$map</var> was not of type array.
  326.     * 
  327.     * @see      getMap()
  328.     * @see      useMap()
  329.     * 
  330.     * @throws   PEAR_Error
  331.     * @access   public
  332.     * @return   mixed       true on success or PEAR_Error
  333.     */
  334.     function setMap($map = array())
  335.     {
  336.         if (!is_array($map)) {
  337.             return PEAR::raiseError(
  338.                 sprintf(FILE_PASSWD_E_PARAM_MUST_BE_ARRAY_STR, '$map'),
  339.                 FILE_PASSWD_E_PARAM_MUST_BE_ARRAY
  340.             );
  341.         }
  342.         $this->_map = $map;
  343.         return true;
  344.     }
  345.     
  346.     /**
  347.     * Get the 'name map' which is used for the extra properties of the user
  348.     *
  349.     * @see      setMap()
  350.     * @see      useMap()
  351.     * 
  352.     * @access public
  353.     * @return array
  354.     */
  355.     function getMap()
  356.     {
  357.         return $this->_map;
  358.     }
  359.     
  360.     /**
  361.     * If the passwords of this passwd file are shadowed in another file.
  362.     *
  363.     * @access public
  364.     * @return boolean
  365.     */
  366.     function isShadowed()
  367.     {
  368.         return $this->_shadowed;
  369.     }
  370.     
  371.     /**
  372.     * Add an user
  373.     *
  374.     * The username must start with an alphabetical character and must NOT
  375.     * contain any other characters than alphanumerics, the underline and dash.
  376.     * 
  377.     * If you use the 'name map' you should also use these naming in
  378.     * the supplied extra array, because your values would get mixed up
  379.     * if they are in the wrong order, which is always true if you
  380.     * DON'T use the 'name map'!
  381.     * 
  382.     * So be warned and USE the 'name map'!
  383.     * 
  384.     * If the passwd file is shadowed, the user will be added though, but
  385.     * with an 'x' as password, and a PEAR_Error will be returned, too.
  386.     * 
  387.     * Returns a PEAR_Error if:
  388.     *   o user already exists
  389.     *   o user contains illegal characters
  390.     *   o encryption mode is not supported
  391.     *   o passwords are shadowed in another file
  392.     *   o any element of the <var>$extra</var> array contains a colon (':')
  393.     * 
  394.     * @throws PEAR_Error
  395.     * @access public
  396.     * @return mixed true on success or PEAR_Error
  397.     * @param  string    $user   the name of the user to add
  398.     * @param  string    $pass   the password of the user to add
  399.     * @param  array     $extra  extra properties of user to add
  400.     */
  401.     function addUser($user, $pass, $extra = array())
  402.     {
  403.         if ($this->userExists($user)) {
  404.             return PEAR::raiseError(
  405.                 sprintf(FILE_PASSWD_E_EXISTS_ALREADY_STR, 'User ', $user),
  406.                 FILE_PASSWD_E_EXISTS_ALREADY
  407.             );
  408.         }
  409.         if (!preg_match($this->_pcre, $user)) {
  410.             return PEAR::raiseError(
  411.                 sprintf(FILE_PASSWD_E_INVALID_CHARS_STR, 'User ', $user),
  412.                 FILE_PASSWD_E_INVALID_CHARS
  413.             );
  414.         }
  415.         if (!is_array($extra)) {
  416.             setType($extra, 'array');
  417.         }
  418.         foreach ($extra as $e){
  419.             if (strstr($e, ':')) {
  420.             return PEAR::raiseError(
  421.                 sprintf(FILE_PASSWD_E_INVALID_CHARS_STR, 'Property ', $e),
  422.                 FILE_PASSWD_E_INVALID_CHARS
  423.             );
  424.             }
  425.         }
  426.         
  427.         /**
  428.         * If passwords of the passwd file are shadowed, 
  429.         * the password of the user will be set to 'x'.
  430.         */
  431.         if ($this->_shadowed) {
  432.             $pass = 'x';
  433.         } else {
  434.             $pass = $this->_genPass($pass);
  435.             if (PEAR::isError($pass)) {
  436.                 return $pass;
  437.             }
  438.         }
  439.         
  440.         /**
  441.         * If you don't use the 'name map' the user array will be numeric.
  442.         */
  443.         if (!$this->_usemap) {
  444.             array_unshift($extra, $pass);
  445.             $this->_users[$user] = $extra;
  446.         } else {
  447.             $map = $this->_map;
  448.             array_unshift($map, 'pass');
  449.             $extra['pass'] = $pass;
  450.             foreach ($map as $key){
  451.                 $this->_users[$user][$key] = @$extra[$key];
  452.             }
  453.         }
  454.         
  455.         /**
  456.         * Raise a PEAR_Error if passwords are shadowed.
  457.         */
  458.         if ($this->_shadowed) {
  459.             return PEAR::raiseError(
  460.                 'Password has been set to \'x\' because they are '.
  461.                 'shadowed in another file.', 0
  462.             );
  463.         }
  464.         return true;
  465.     }
  466.     
  467.     /**
  468.     * Modify properties of a certain user
  469.     *
  470.     * # DON'T MODIFY THE PASSWORD WITH THIS METHOD!
  471.     * 
  472.     * You should use this method only if the 'name map' is used, too.
  473.     * 
  474.     * Returns a PEAR_Error if:
  475.     *   o user doesn't exist
  476.     *   o any property contains a colon (':')
  477.     * 
  478.     * @see      changePasswd()
  479.     * 
  480.     * @throws   PEAR_Error
  481.     * @access   public
  482.     * @return   mixed       true on success or PEAR_Error
  483.     * @param    string      $user           the user to modify
  484.     * @param    array       $properties     an associative array of 
  485.     *                                       properties to modify
  486.     */
  487.     function modUser($user, $properties = array())
  488.     {
  489.         if (!$this->userExists($user)) {
  490.             return PEAR::raiseError(
  491.                 sprintf(FILE_PASSWD_E_EXISTS_NOT_STR, 'User ', $user),
  492.                 FILE_PASSWD_E_EXISTS_NOT
  493.             );
  494.         }
  495.         
  496.         if (!is_array($properties)) {
  497.             setType($properties, 'array');
  498.         }
  499.         
  500.         foreach ($properties as $key => $value){
  501.             if (strstr($value, ':')) {
  502.                 return PEAR::raiseError(
  503.                     sprintf(FILE_PASSWD_E_INVALID_CHARS_STR, 'User ', $user),
  504.                     FILE_PASSWD_E_INVALID_CHARS
  505.                 );
  506.             }
  507.             $this->_users[$user][$key] = $value;
  508.         }
  509.         
  510.         return true;
  511.     }
  512.     
  513.     /**
  514.     * Change the password of a certain user
  515.     *
  516.     * Returns a PEAR_Error if:
  517.     *   o user doesn't exists
  518.     *   o passwords are shadowed in another file
  519.     *   o encryption mode is not supported
  520.     * 
  521.     * @throws PEAR_Error
  522.     * @access public
  523.     * @return mixed true on success or PEAR_Error
  524.     * @param string $user   the user whose password should be changed
  525.     * @param string $pass   the new plaintext password
  526.     */
  527.     function changePasswd($user, $pass)
  528.     {
  529.         if ($this->_shadowed) {
  530.             return PEAR::raiseError(
  531.                 'Passwords of this passwd file are shadowed.', 
  532.                 0
  533.             );
  534.         }
  535.         
  536.         if (!$this->userExists($user)) {
  537.             return PEAR::raiseError(
  538.                 sprintf(FILE_PASSWD_E_EXISTS_NOT_STR, 'User ', $user),
  539.                 FILE_PASSWD_E_EXISTS_NOT
  540.             );
  541.         }
  542.         
  543.         $pass = $this->_genPass($pass);
  544.         if (PEAR::isError($pass)) {
  545.             return $pass;
  546.         }
  547.         
  548.         if ($this->_usemap) {
  549.             $this->_users[$user]['pass'] = $pass;
  550.         } else {
  551.             $this->_users[$user][0] = $pass;
  552.         }
  553.         
  554.         return true;
  555.     }
  556.     
  557.     /**
  558.     * Verify the password of a certain user
  559.     * 
  560.     * Returns a PEAR_Error if:
  561.     *   o user doesn't exist
  562.     *   o encryption mode is not supported
  563.     *
  564.     * @throws PEAR_Error
  565.     * @access public
  566.     * @return mixed true if passwors equal, false if they don't or PEAR_Error
  567.     * @param  string    $user   the user whose password should be verified
  568.     * @param  string    $pass   the password to verify
  569.     */
  570.     function verifyPasswd($user, $pass)
  571.     {
  572.         if (!$this->userExists($user)) {
  573.             return PEAR::raiseError(
  574.                 sprintf(FILE_PASSWD_E_EXISTS_NOT_STR, 'User ', $user),
  575.                 FILE_PASSWD_E_EXISTS_NOT
  576.             );
  577.         }
  578.         $real = 
  579.             $this->_usemap ? 
  580.             $this->_users[$user]['pass'] : 
  581.             $this->_users[$user][0]
  582.         ;
  583.         return ($real === $this->_genPass($pass, $real));
  584.     }
  585.     
  586.     /**
  587.     * Generate crypted password from the plaintext password
  588.     *
  589.     * Returns a PEAR_Error if actual encryption mode is not supported.
  590.     * 
  591.     * @throws PEAR_Error
  592.     * @access private
  593.     * @return mixed     the crypted password or PEAR_Error
  594.     * @param  string    $pass   the plaintext password
  595.     * @param  string    $salt   the crypted password from which to gain the salt
  596.     * @param  string    $mode   the encryption mode to use; don't set, because
  597.     *                           it's usually taken from File_Passwd_Unix::_mode
  598.     */
  599.     function _genPass($pass, $salt = null, $mode = null)
  600.     {
  601.         static $crypters;
  602.         if (!isset($crypters)) {
  603.             $crypters = get_class_methods('File_Passwd');
  604.         }
  605.         
  606.         $mode = !isset($mode) ? strToLower($this->_mode) : strToLower($mode);
  607.         $func = 'crypt_' . $mode;
  608.         
  609.         if (!in_array($func, $crypters)) {
  610.             return PEAR::raiseError(
  611.                 sprintf(FILE_PASSWD_E_INVALID_ENC_MODE_STR, $mode),
  612.                 FILE_PASSWD_E_INVALID_ENC_MODE
  613.             );
  614.         }
  615.         
  616.         return call_user_func(array('File_Passwd', $func), $pass, $salt);
  617.     }
  618.     
  619.     /**
  620.     * Generate Password
  621.     *
  622.     * Returns PEAR_Error FILE_PASSD_E_INVALID_ENC_MODE if the supplied
  623.     * encryption mode is not supported.
  624.     *
  625.     * @see File_Passwd
  626.     * @static
  627.     * @access   public
  628.     * @return   mixed   The crypted password on success or PEAR_Error on failure.
  629.     * @param    string  $pass The plaintext password.
  630.     * @param    string  $mode The encryption mode to use.
  631.     * @param    string  $salt The salt to use.
  632.     */
  633.     function generatePassword($pass, $mode = 'md5', $salt = null)
  634.     {
  635.         if (!isset($mode)) {
  636.             return PEAR::raiseError(
  637.                 sprintf(FILE_PASSWD_E_INVALID_ENC_MODE_STR, '<NULL>'),
  638.                 FILE_PASSWD_E_INVALID_ENC_MODE                
  639.             );
  640.         }
  641.         return File_Passwd_Unix::_genPass($pass, $salt, $mode);
  642.     }
  643.     
  644. }
  645. ?>